agora inbox for [email protected]
help / color / mirror / Atom feedRe: Open 7.3 items
965+ messages / 2 participants
[nested] [flat]
* Re: Open 7.3 items
@ 2002-08-07 08:02 Dave Page <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Dave Page @ 2002-08-07 08:02 UTC (permalink / raw)
To: Bruce Momjian <[email protected]>; +Cc: pgsql-hackers
> -----Original Message-----
> From: Bruce Momjian [mailto:[email protected]]
> Sent: 07 August 2002 02:09
> To: Peter Eisentraut
> Cc: Marc G. Fournier; Ron Snyder; Neil Conway; PostgreSQL-development
> Subject: Re: [HACKERS] Open 7.3 items
>
>
>
> It had such limited usefulness ('password' only, only
> crypted-hashed passwords in the file) that it doesn't make
> much sense to resurect it.
>
> To directly address your point, I don't think this new
> feature will be used enough to add the capability to the user
> admin commands.
>
> I know you object, so I am going to ask for a vote.
>
> OK, here is the request for vote. Do we want:
>
> 1) the old secondary passwords re-added
> 2) the new prefixing of the database name to the
> username when enabled
> 3) do nothing
2 please. I have used secondary passwords in the past, but 2 would do
the same job and seems cleaner.
Regards, Dave.
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 965+ messages in thread
From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)
Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.
A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.
Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.
Author: Antonin Houska <[email protected]>
Reported-by: Justin Pryzby <[email protected]>
Reviewed-by: Chao Li <[email protected]>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
src/backend/commands/repack_worker.c | 1 -
.../test_plan_advice/t/001_replan_regress.pl | 1 +
src/test/regress/expected/cluster.out | 20 +++++++++++++++++--
src/test/regress/sql/cluster.sql | 11 ++++++++--
4 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
/*
* Make sure we can use logical decoding.
*/
- CheckSlotPermissions();
CheckLogicalDecodingRequirements(true);
/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
# Set up our desired configuration.
$node->append_conf('postgresql.conf', <<EOM);
shared_preload_libraries='test_plan_advice'
+wal_level=replica
pg_plan_advice.always_explain_supplied_advice=false
pg_plan_advice.feedback_warnings=true
EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables
HINT: Consider running the command on individual partitions.
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR: cannot process relation "ptnowner1"
+HINT: Relation "ptnowner1" has no identity index.
RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
ptnowner2 | t
(3 rows)
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+ relname | ?column?
+-----------+----------
+ ptnowner | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
DROP TABLE ptnowner;
DROP ROLE regress_ptnowner;
-- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
DROP TABLE clstrpart;
-- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
CREATE INDEX ptnowner_i_idx ON ptnowner(i);
CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+ JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
DROP TABLE ptnowner;
--
2.47.3
--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"
^ permalink raw reply [nested|flat] 965+ messages in thread
end of thread, other threads:[~2026-04-20 09:38 UTC | newest]
Thread overview: 965+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2002-08-07 08:02 Re: Open 7.3 items Dave Page <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Álvaro Herrera <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox